#pragma once
#include "requestor.hpp"
#include <unordered_set>

namespace bitrpc
{
    namespace client
    {
        // 服务注册
        class Provider
        {
        public:
            using ptr = std::shared_ptr<Provider>;
            Provider(const Requestor::ptr &requestor)
                : _requestor(requestor)
            {
            }
            // 注册服务
            bool registryMethod(const BaseConnection::ptr &conn, const std::string &method, const Address &host)
            {
                //ELOG("%s 服务注册中...", method.c_str());
                auto msg_req = MessageFactory::create<ServiceRequest>();
                msg_req->setMethod(method);
                msg_req->setHost(host);
                msg_req->setId(UUID::uuid());
                msg_req->setMType(MType::REQ_SERVICE);
                msg_req->setOptype(ServiceOptype::SERVICE_REGISTRY); // 注册服务
                BaseMessage::ptr msg_rsp;                            // 服务响应结果
                bool ret = _requestor->send(conn, msg_req, msg_rsp); // 服务发送
                if (ret == false)
                {
                    ELOG("%s 服务注册失败!", method.c_str());
                    return false;
                }
                auto service_rsp = std::dynamic_pointer_cast<ServiceResponse>(msg_rsp); // 将响应结果转换为服务响应
                if (service_rsp.get() == nullptr)
                {
                    ELOG("响应类型向下转换失败!");
                    return false;
                }
                if (service_rsp->rcode() != RCode::RCODE_OK)
                {
                    ELOG("服务注册失败,原因: %s", errReason(service_rsp->rcode()).c_str());
                    return false;
                }
                return true;
            }

        private:
            Requestor::ptr _requestor;
        };

        class MethodHost
        {
        public:
            using ptr = std::shared_ptr<MethodHost>;
            MethodHost() : _idx(0) {}
            MethodHost(const std::vector<Address> &hosts)
                : _hosts(hosts.begin(), hosts.end()), _idx(0)
            {
            }
            // 添加服务器地址
            void appendHost(const Address &host)
            {
                // 中途收到服务上线请求后被调用
                std::unique_lock<std::mutex> lock(_mtx);
                _hosts.push_back(host); // 插入服务器地址
            }
            // 选择服务器地址
            Address chooseHost()
            {
                std::unique_lock<std::mutex> lock(_mtx);
                size_t pos = _idx++ % _hosts.size();
                return _hosts[pos];
            }
            // 判断地址是否为空
            bool empty()
            {
                std::unique_lock<std::mutex> lock(_mtx);
                return _hosts.empty();
            }
            // 删除服务器地址
            void removeHost(const Address &host)
            {
                std::unique_lock<std::mutex> lock(_mtx);
                for (auto it = _hosts.begin(); it != _hosts.end(); it++)
                {
                    if (*it == host)
                    {
                        _hosts.erase(it);
                        break;
                    }
                }
            }

        private:
            std::mutex _mtx;
            size_t _idx;                 // 当前选择的服务器地址
            std::vector<Address> _hosts; // 服务器地址列表
        };
        // 服务发现
        class Discoverer
        {
        public:
            using OfflineCallback = std::function<void(const Address &)>; // 服务器下线回调函数
            using ptr = std::shared_ptr<Discoverer>;
            Discoverer(const Requestor::ptr &requestor, const OfflineCallback &cb)
                : _requestor(requestor), _offline_callback(cb)
            {
            }
            // 服务发现
            bool serviceDiscovery(const BaseConnection::ptr &conn, const std::string &method, Address &host)
            {
                //DLOG("服务发现: %s", method.c_str());
                {
                    // 当前所保管的提供者信息存在，则直接返回地址
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _method_hosts.find(method);
                    if (it != _method_hosts.end())
                    {
                        if (it->second->empty() == false)
                        {
                            host = it->second->chooseHost();
                            return true;
                        }
                    }
                }

                // 当前服务的提供者为空
                auto msg_req = MessageFactory::create<ServiceRequest>();
                msg_req->setMethod(method);
                msg_req->setId(UUID::uuid());
                msg_req->setMType(MType::REQ_SERVICE);
                msg_req->setOptype(ServiceOptype::SERVICE_DISCOVERY); // 服务发现
                BaseMessage::ptr msg_rsp;
                //DLOG("发送服务发现请求！");
                bool ret = _requestor->send(conn, msg_req, msg_rsp); // 发送服务发现请求
                if (ret == false)
                {
                    ELOG("服务发现失败");
                    return false;
                }
                auto service_rsp = std::dynamic_pointer_cast<ServiceResponse>(msg_rsp);
                if (!service_rsp)
                {
                    ELOG("服务发现失败,响应类型转换失败!");
                    return false;
                }
                if (service_rsp->rcode() != RCode::RCODE_OK)
                {
                    ELOG("服务发现失败,响应码错误! %s", errReason(service_rsp->rcode()).c_str());
                    return false;
                }
                // 能走到这里，代表当前是还没有对应的服务提供给主机的
                std::unique_lock<std::mutex> lock(_mutex);
                auto method_host = std::make_shared<MethodHost>(service_rsp->hosts());
                if (method_host->empty())
                {
                    ELOG("%s 服务发现失败,没有能提供服务的主机!", method.c_str());
                    return false;
                }
                host = method_host->chooseHost();    // 选择一个主机
                _method_hosts[method] = method_host; // 保存服务提供者信息 如果已经存在，则更新,不存在则插入
                return true;
            }

            // 这个接口是提供给Dispatcher模块进行服务上线下线请求处理的回调函数
            void onServiceRequest(const BaseConnection::ptr &conn, const ServiceRequest::ptr &msg)
            {
                // 1. 判断是上线还是下线请求，如果都不是那就不用处理了
                auto optype = msg->optype();
                std::string method = msg->method();
                std::unique_lock<std::mutex> lock(_mutex);
                if (optype == ServiceOptype::SERVICE_ONLINE)
                {
                    // 2. 上线请求：找到MethodHost，向其中新增一个主机地址
                    auto it = _method_hosts.find(method);
                    if (it == _method_hosts.end())
                    {
                        // 说明曾经有过这个服务，但是现在已经下线了，所以需要重新注册
                        auto method_host = std::make_shared<MethodHost>(); // 创建一个MethodHost
                        method_host->appendHost(msg->host());              // 新增一个主机地址
                        _method_hosts[method] = method_host;               // 保存服务提供者信息
                    }
                    else
                    {
                        // 说明服务依然存在，只需要新增一个主机地址即可
                        it->second->appendHost(msg->host());
                    }
                }
                else if (optype == ServiceOptype::SERVICE_OFFLINE)
                {
                    // 3. 下线请求：找到MethodHost，从其中删除一个主机地址
                    auto it = _method_hosts.find(method);
                    if (it == _method_hosts.end())
                    {
                        return;
                    }
                    it->second->removeHost(msg->host()); // 删除一个主机地址
                    _offline_callback(msg->host());      // 调用服务下线回调函数
                }
            }

        private:
            OfflineCallback _offline_callback; // 服务下线回调函数
            std::mutex _mutex;
            std::unordered_map<std::string, MethodHost::ptr> _method_hosts; // 服务名(Add)与服务器地址列表的映射
            Requestor::ptr _requestor;
        };
    }
}